use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn call() -> impl IntoIterator<Item = &'static str> {
    [
        "/user/repos",
        "/repos/rust-lang/rust/stargazers",
        "/orgs/rust-lang/public_members/nikomatsakis",
        "/repos/rust-lang/rust/releases/1.51.0",
    ]
}

fn compare(c: &mut Criterion) {
    let mut group = c.benchmark_group("Compare");

    let mut matchit = matchit::Router::new();
    for route in register!(colon) {
        matchit.insert(route, true).unwrap();
    }

    group.bench_function("matchit", |b| {
        b.iter(|| {
            for route in black_box(call()) {
                black_box(matchit.at(route).unwrap());
            }
        });
    });

    let mut xitca = xitca_router::Router::new();
    for route in register!(colon) {
        xitca.insert(route, true).unwrap();
    }
    group.bench_function("xitca", |b| {
        b.iter(|| {
            for route in black_box(call()) {
                black_box(xitca.at(route).unwrap());
            }
        });
    });

    group.finish();
}

fn compare_erase(c: &mut Criterion) {
    let mut group = c.benchmark_group("Compare lifetime erase");

    let mut matchit = matchit::Router::new();
    for route in register!(colon) {
        matchit.insert(route, true).unwrap();
    }

    group.bench_function("matchit", |b| {
        b.iter(|| {
            for route in black_box(call()) {
                black_box(
                    matchit
                        .at(route)
                        .unwrap()
                        .params
                        .iter()
                        // as of writing the bench this is roughly what axum do to erase lifetime
                        // of params.
                        .map(|(k, v)| (String::from(k), String::from(v)))
                        .collect::<Vec<_>>(),
                );
            }
        });
    });

    let mut xitca = xitca_router::Router::new();
    for route in register!(colon) {
        xitca.insert(route, true).unwrap();
    }
    group.bench_function("xitca", |b| {
        b.iter(|| {
            for route in black_box(call()) {
                black_box(xitca.at(route).unwrap().params);
            }
        });
    });

    group.finish();
}

fn compare_empty_path(c: &mut Criterion) {
    let mut group = c.benchmark_group("Compare empty Params");

    let mut matchit = matchit::Router::new();
    matchit.insert("/", true).unwrap();
    group.bench_function("matchit", |b| {
        b.iter(|| {
            black_box(matchit.at(black_box("/")).unwrap());
        });
    });

    let mut xitca = xitca_router::Router::new();
    xitca.insert("/", true).unwrap();
    group.bench_function("xitca", |b| {
        b.iter(|| black_box(xitca.at(black_box("/")).unwrap()));
    });

    group.finish();
}

criterion_group!(benches, compare, compare_erase, compare_empty_path);
criterion_main!(benches);

macro_rules! register {
    (colon) => {{
        register!(finish => ":p1", ":p2", ":p3", ":p4")
    }};
    (brackets) => {{
        register!(finish => "{p1}", "{p2}", "{p3}", "{p4}")
    }};
    (regex) => {{
        register!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
    }};
    (finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
        [
           "/authorizations",
            concat!("/authorizations/", $p1),
            concat!("/applications/", $p1, "/tokens/", $p2),
            "/events",
            concat!("/repos/", $p1, "/", $p2, "/events"),
            concat!("/networks/", $p1, "/", $p2, "/events"),
            concat!("/orgs/", $p1, "/events"),
            concat!("/users/", $p1, "/received_events"),
            concat!("/users/", $p1, "/received_events/public"),
            concat!("/users/", $p1, "/events"),
            concat!("/users/", $p1, "/events/public"),
            concat!("/users/", $p1, "/events/orgs/", $p2),
            "/feeds",
            "/notifications",
            concat!("/repos/", $p1, "/", $p2, "/notifications"),
            concat!("/notifications/threads/", $p1),
            concat!("/notifications/threads/", $p1, "/subscription"),
            concat!("/repos/", $p1, "/", $p2, "/stargazers"),
            concat!("/users/", $p1, "/starred"),
            "/user/starred",
            concat!("/user/starred/", $p1, "/", $p2),
            concat!("/repos/", $p1, "/", $p2, "/subscribers"),
            concat!("/users/", $p1, "/subscriptions"),
           "/user/subscriptions",
            concat!("/repos/", $p1, "/", $p2, "/subscription"),
            concat!("/user/subscriptions/", $p1, "/", $p2),
            concat!("/users/", $p1, "/gists"),
           "/gists",
            concat!("/gists/", $p1),
            concat!("/gists/", $p1, "/star"),
            concat!("/repos/", $p1, "/", $p2, "/git/blobs/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/git/commits/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/git/refs"),
            concat!("/repos/", $p1, "/", $p2, "/git/tags/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/git/trees/", $p3),
            "/issues",
            "/user/issues",
            concat!("/orgs/", $p1, "/issues"),
            concat!("/repos/", $p1, "/", $p2, "/issues"),
            concat!("/repos/", $p1, "/", $p2, "/issues/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/assignees"),
            concat!("/repos/", $p1, "/", $p2, "/assignees/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/comments"),
            concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/events"),
            concat!("/repos/", $p1, "/", $p2, "/labels"),
            concat!("/repos/", $p1, "/", $p2, "/labels/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/issues/", $p3, "/labels"),
            concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3, "/labels"),
            concat!("/repos/", $p1, "/", $p2, "/milestones/"),
            concat!("/repos/", $p1, "/", $p2, "/milestones/", $p3),
            "/emojis",
            "/gitignore/templates",
            concat!("/gitignore/templates/", $p1),
            "/meta",
            "/rate_limit",
            concat!("/users/", $p1, "/orgs"),
            "/user/orgs",
            concat!("/orgs/", $p1),
            concat!("/orgs/", $p1, "/members"),
            concat!("/orgs/", $p1, "/members", $p2),
            concat!("/orgs/", $p1, "/public_members"),
            concat!("/orgs/", $p1, "/public_members/", $p2),
            concat!("/orgs/", $p1, "/teams"),
            concat!("/teams/", $p1),
            concat!("/teams/", $p1, "/members"),
            concat!("/teams/", $p1, "/members", $p2),
            concat!("/teams/", $p1, "/repos"),
            concat!("/teams/", $p1, "/repos/", $p2, "/", $p3),
            "/user/teams",
            concat!("/repos/", $p1, "/", $p2, "/pulls"),
            concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/commits"),
            concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/files"),
            concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/merge"),
            concat!("/repos/", $p1, "/", $p2, "/pulls/", $p3, "/comments"),
            "/user/repos",
            concat!("/users/", $p1, "/repos"),
            concat!("/orgs/", $p1, "/repos"),
            "/repositories",
            concat!("/repos/", $p1, "/", $p2),
            concat!("/repos/", $p1, "/", $p2, "/contributors"),
            concat!("/repos/", $p1, "/", $p2, "/languages"),
            concat!("/repos/", $p1, "/", $p2, "/teams"),
            concat!("/repos/", $p1, "/", $p2, "/tags"),
            concat!("/repos/", $p1, "/", $p2, "/branches"),
            concat!("/repos/", $p1, "/", $p2, "/branches/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/collaborators"),
            concat!("/repos/", $p1, "/", $p2, "/collaborators/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/comments"),
            concat!("/repos/", $p1, "/", $p2, "/commits/", $p3, "/comments"),
            concat!("/repos/", $p1, "/", $p2, "/commits"),
            concat!("/repos/", $p1, "/", $p2, "/commits/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/readme"),
            concat!("/repos/", $p1, "/", $p2, "/keys"),
            concat!("/repos/", $p1, "/", $p2, "/keys", $p3),
            concat!("/repos/", $p1, "/", $p2, "/downloads"),
            concat!("/repos/", $p1, "/", $p2, "/downloads", $p3),
            concat!("/repos/", $p1, "/", $p2, "/forks"),
            concat!("/repos/", $p1, "/", $p2, "/hooks"),
            concat!("/repos/", $p1, "/", $p2, "/hooks", $p3),
            concat!("/repos/", $p1, "/", $p2, "/releases"),
            concat!("/repos/", $p1, "/", $p2, "/releases/", $p3),
            concat!("/repos/", $p1, "/", $p2, "/releases/", $p3, "/assets"),
            concat!("/repos/", $p1, "/", $p2, "/stats/contributors"),
            concat!("/repos/", $p1, "/", $p2, "/stats/commit_activity"),
            concat!("/repos/", $p1, "/", $p2, "/stats/code_frequency"),
            concat!("/repos/", $p1, "/", $p2, "/stats/participation"),
            concat!("/repos/", $p1, "/", $p2, "/stats/punch_card"),
            concat!("/repos/", $p1, "/", $p2, "/statuses/", $p3),
            "/search/repositories",
            "/search/code",
            "/search/issues",
            "/search/users",
            concat!("/legacy/issues/search/", $p1, "/", $p2, "/", $p3, "/", $p4),
            concat!("/legacy/repos/search/", $p1),
            concat!("/legacy/user/search/", $p1),
            concat!("/legacy/user/email/", $p1),
            concat!("/users/", $p1),
            "/user",
            "/users",
            "/user/emails",
            concat!("/users/", $p1, "/followers"),
            "/user/followers",
            concat!("/users/", $p1, "/following"),
            "/user/following",
            concat!("/user/following/", $p1),
            concat!("/users/", $p1, "/following", $p2),
            concat!("/users/", $p1, "/keys"),
            "/user/keys",
            concat!("/user/keys/", $p1),
        ]
    }};
}

use register;
